#pragma once
#include "Common.h"
#include "CentralCache.h"

class ThreadCache
{
public:
	void* Allocate(size_t size)
	{
		assert(size <= MAX_BYTES);
		size_t alignSize = SizeClass::RoundUp(size);
		size_t index = SizeClass::Index(size);

		if (!_freelist[index].Empty())
		{
			return _freelist[index].Pop();
		}
		else
		{
			return FetchFromCentralCache(index, alignSize);
		}
	}

	void* FetchFromCentralCache(size_t index, size_t size)
	{
		size_t batchNum = (std::min)(_freelist[index].MaxSize(), SizeClass::NumMoveSize(size));
		if (batchNum == _freelist[index].MaxSize())
		{
			_freelist[index].MaxSize() += 1;
		}

		void* start=nullptr;
		void* end=nullptr;
		size_t actualNum=CentralCache::GetInstance()->FetchRangeObj(start, end, batchNum, size);
		assert(actualNum > 0);
		if (actualNum == 1)
		{
			assert(start == end);
			return start;
		}
		else
		{
			_freelist[index].PushRange(Nextobj(start), end,actualNum-1);
			return start;
		}
	}

	void Deallocate(void* ptr, size_t size)
	{
		assert(ptr);
		assert(size <= MAX_BYTES);

		size_t index = SizeClass::Index(size);
		_freelist[index].Push(ptr);

		if (_freelist[index].Size() >= _freelist[index].MaxSize())
		{
			ListTooLong(_freelist[index], size);
		}
	}

	void ListTooLong(FreeList& list, size_t size)
	{
		void* start = nullptr;
		void* end = nullptr;
		list.PopRange(start,end,list.MaxSize());

		CentralCache::GetInstance()->ReleaseListToSpans(start, size);
	}

	

private:
	FreeList _freelist[NFREE_LISTS];
};

static _declspec(thread) ThreadCache* pTLSThreadCache=nullptr;